package tw.right929.proxy
{
	import flash.utils.*;
	
	use namespace flash_proxy
	
	/**
	 * <p>
	 * 達成條件時，發動函式whenInDo.call(thisObjectForIn)，<br/>
	 * 從達成條件變成未達成時，發動函式whenOutDo.call(thisObjectForOut)。
	 * </p>
	 */
	public dynamic class Trigger extends Proxy
	{
		private var _propertyMap:Object		=	new Object();
		
		private var _triggerCount:int	=	0;
		private var _map:Dictionary	=	new Dictionary();
		
		public function Trigger()
		{
			super();
		}
		
		/**
		 * 新增一個條件觸發式。
		 * 若加入時，Trigger已是滿足條件的狀態，會直接發動函式whenInDo.call(thisObjectForIn)。
		 */
		public function $addTrigger(conditions:* , whenInDo:Function , whenOutDo:Function , thisObjectForIn:*=null , thisObjectForOut:*=null):*
		{
			_triggerCount++;
			_map[_triggerCount]	=	new $ConditionInfo(conditions , whenInDo , whenOutDo , thisObjectForIn , thisObjectForOut);
			_map[_triggerCount].testConditions(_propertyMap , _propertyMap);
			return _triggerCount;
		}
		
		/**
		 * 移除一個條件觸發式。
		 */
		public function $removeTrigger(triggerID:*):void
		{
			_map[triggerID].die();
			delete _map[triggerID];
		}
		
		/**
		 * 一次修改許多絛件式。
		 */
		public function $setConditions(conditions:*):void
		{
			for(var p:String in conditions)
				_propertyMap[p]	=	conditions[p];
			_update(conditions);
		}
		
		/**
		 * 把物件清空，方便回收。
		 */
		public function die():void
		{
			for(var triggerID:* in _map)
				$removeTrigger(triggerID);
			
			for(var p:* in _propertyMap)
				delete _propertyMap[p];
			
			_propertyMap	=	null;
			_map	=	null;
		}
		
		/**
		 * conditions為改變的條件。
		 * 檢查所有觸發式，若有符合，則發動。
		 */
		private function _update(conditions:*):void
		{
			for each(var conditionInfo:$ConditionInfo in _map)
			{
				conditionInfo.testConditions(_propertyMap , conditions);
			}
		}
		
		override flash_proxy function setProperty(name:*, value:*):void
		{
			if(this[name] == value)
				return;
			_propertyMap[name]	=	value;
			var conditions:Object	=	new Object();
			conditions[name]	=	value;
			_update(conditions);
		}
		
		override flash_proxy function deleteProperty(name:*):Boolean
		{
			return delete _propertyMap[name]; 
			var conditions:Object	=	new Object();
			conditions[name]	=	false;
			_update(conditions);
		}
		
		override flash_proxy function getProperty(name:*):*
		{
			return _propertyMap[name];
		}
		
		override flash_proxy function hasProperty(name:*):Boolean
		{
			return name in _propertyMap;
		}
	}
}

class $ConditionInfo
{
	public var conditions:*;
	public var whenInDo:Function;
	public var whenOutDo:Function;
	public var thisObjectForIn:*;
	public var thisObjectForOut:*;
	
	private var _inConditions:Boolean	=	false;
	
	public function $ConditionInfo(conditions:* , whenInDo:Function , whenOutDo:Function , thisObjectForIn:*=null , thisObjectForOut:*=null)
	{
		this.conditions	=	conditions;
		this.whenInDo	=	whenInDo;
		this.whenOutDo	=	whenOutDo;
		this.thisObjectForIn	=	thisObjectForIn;
		this.thisObjectForOut	=	thisObjectForOut;
	}
	
	public function testConditions(conditionsMap:Object , change:*):void
	{
		if(checkChange(change))
		{
			if(_inConditions)
			{
				_inConditions	=	false;
				if(whenOutDo != null)
					whenOutDo.call(thisObjectForOut);
			}
			else
			{
				for(var p:String in conditions)
				{
					if(conditionsMap[p] == undefined)
						return;
					if(conditionsMap[p] != conditions[p])
						return;
				}
				_inConditions	=	true;
				if(whenInDo != null)
					whenInDo.call(thisObjectForIn);
			}
		}
	}
	
	public function die():void
	{
		conditions	=	null;
		whenInDo	=	null;
		whenOutDo	=	null;
		thisObjectForIn	=	null;
		thisObjectForOut	=	null;
	}
	
	private function checkChange(change:*):Boolean
	{
		for(var p:String in change)
		{
			if(p in conditions)
				return true;
		}
		return false;
	}
}